home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet internetowy / Przegladarki internetowe / Mozilla Seamonkey 1.0.5 pl / seamonkey-1.0.5.pl-PL.win32.installer.exe / MAIL.XPI / bin / chrome / messenger.jar / content / messenger / msgMail3PaneWindow.js < prev    next >
Encoding:
Text File  |  2006-01-26  |  51.9 KB  |  1,586 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  * ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Mozilla Communicator client code, released
  16.  * March 31, 1998.
  17.  *
  18.  * The Initial Developer of the Original Code is
  19.  * Netscape Communications Corporation.
  20.  * Portions created by the Initial Developer are Copyright (C) 1998-1999
  21.  * the Initial Developer. All Rights Reserved.
  22.  *
  23.  * Contributor(s):
  24.  *   Jan Varga (varga@ku.sk)
  25.  *   Hσkan Waara (hwaara@chello.se)
  26.  *   Neil Rashbrook (neil@parkwaycc.co.uk)
  27.  *   Seth Spitzer <sspitzer@netscape.com>
  28.  *
  29.  * Alternatively, the contents of this file may be used under the terms of
  30.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  31.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32.  * in which case the provisions of the GPL or the LGPL are applicable instead
  33.  * of those above. If you wish to allow use of your version of this file only
  34.  * under the terms of either the GPL or the LGPL, and not to allow others to
  35.  * use your version of this file under the terms of the MPL, indicate your
  36.  * decision by deleting the provisions above and replace them with the notice
  37.  * and other provisions required by the GPL or the LGPL. If you do not delete
  38.  * the provisions above, a recipient may use your version of this file under
  39.  * the terms of any one of the MPL, the GPL or the LGPL.
  40.  *
  41.  * ***** END LICENSE BLOCK ***** */
  42.  
  43. /* This is where functions related to the 3 pane window are kept */
  44.  
  45. // from MailNewsTypes.h
  46. const nsMsgKey_None = 0xFFFFFFFF;
  47. const nsMsgViewIndex_None = 0xFFFFFFFF;
  48. const kMailCheckOncePrefName = "mail.startup.enabledMailCheckOnce";
  49.  
  50. // from nsMsgFolderFlags.h
  51. const MSG_FOLDER_FLAG_ELIDED = 0x10;
  52.  
  53. var gFolderTree; 
  54. var gMessagePane;
  55. var gThreadTree;
  56. var gSearchInput;
  57.  
  58. var gThreadAndMessagePaneSplitter = null;
  59. var gUnreadCount = null;
  60. var gTotalCount = null;
  61.  
  62. var gCurrentLoadingFolderURI;
  63. var gCurrentFolderToReroot;
  64. var gCurrentLoadingFolderSortType = 0;
  65. var gCurrentLoadingFolderSortOrder = 0;
  66. var gCurrentLoadingFolderViewType = 0;
  67. var gCurrentLoadingFolderViewFlags = 0;
  68. var gRerootOnFolderLoad = false;
  69. var gCurrentDisplayedMessage = null;
  70. var gNextMessageAfterDelete = null;
  71. var gNextMessageAfterLoad = null;
  72. var gNextMessageViewIndexAfterDelete = -2;
  73. var gCurrentlyDisplayedMessage=nsMsgViewIndex_None;
  74. var gStartFolderUri = null;
  75. var gStartMsgKey = nsMsgKey_None;
  76. var gSearchEmailAddress = null;
  77. var gRightMouseButtonDown = false;
  78. // Global var to keep track of which row in the thread pane has been selected
  79. // This is used to make sure that the row with the currentIndex has the selection
  80. // after a Delete or Move of a message that has a row index less than currentIndex.
  81. var gThreadPaneCurrentSelectedIndex = -1;
  82. var gLoadStartFolder = true;
  83.  
  84. // Global var to keep track of if the 'Delete Message' or 'Move To' thread pane
  85. // context menu item was triggered.  This helps prevent the tree view from
  86. // not updating on one of those menu item commands.
  87. var gThreadPaneDeleteOrMoveOccurred = false;
  88.  
  89. //If we've loaded a message, set to true.  Helps us keep the start page around.
  90. var gHaveLoadedMessage;
  91.  
  92. var gDisplayStartupPage = false;
  93.  
  94. var gNotifyDefaultInboxLoadedOnStartup = false;
  95.  
  96. function SelectAndScrollToKey(aMsgKey)
  97. {
  98.   // select the desired message
  99.   // if the key isn't found, we won't select anything
  100.   gDBView.selectMsgByKey(aMsgKey);
  101.   
  102.   // is there a selection?
  103.   // if not, bail out.
  104.   var indicies = GetSelectedIndices(gDBView);
  105.   if (!indicies || !indicies.length)
  106.     return false;
  107.  
  108.   // now scroll to it
  109.   EnsureRowInThreadTreeIsVisible(indicies[0]);
  110.   return true;
  111. }
  112.  
  113. // the folderListener object
  114. var folderListener = {
  115.     OnItemAdded: function(parentItem, item) { },
  116.  
  117.     OnItemRemoved: function(parentItem, item) { },
  118.  
  119.     OnItemPropertyChanged: function(item, property, oldValue, newValue) { },
  120.  
  121.     OnItemIntPropertyChanged: function(item, property, oldValue, newValue) {
  122.       if (item == gMsgFolderSelected) {
  123.         if(property.toString() == "TotalMessages" || property.toString() == "TotalUnreadMessages") {
  124.           UpdateStatusMessageCounts(gMsgFolderSelected);
  125.           UpdateLocationBar(item);
  126.         }      
  127.       }
  128.     },
  129.  
  130.     OnItemBoolPropertyChanged: function(item, property, oldValue, newValue) { },
  131.  
  132.     OnItemUnicharPropertyChanged: function(item, property, oldValue, newValue) { },
  133.     OnItemPropertyFlagChanged: function(item, property, oldFlag, newFlag) { },
  134.  
  135.     OnItemEvent: function(folder, event) {
  136.        var eventType = event.toString();
  137.        if (eventType == "FolderLoaded") {
  138.          if (folder) {
  139.            var msgFolder = folder.QueryInterface(Components.interfaces.nsIMsgFolder);
  140.            var uri = folder.URI;
  141.            var rerootingFolder = (uri == gCurrentFolderToReroot);
  142.            if (rerootingFolder) {
  143.             viewDebug("uri = gCurrentFolderToReroot, setting gQSViewIsDirty\n");
  144.              gQSViewIsDirty = true;
  145.              gCurrentFolderToReroot = null;
  146.              if(msgFolder) {
  147.                msgFolder.endFolderLoading();
  148.                // suppress command updating when rerooting the folder
  149.                // when rerooting, we'll be clearing the selection
  150.                // which will cause us to update commands.
  151.                if (gDBView) {
  152.                  gDBView.suppressCommandUpdating = true;
  153.                  // if the db's view isn't set, something went wrong and we should reroot
  154.                  // the folder, which will re-open the view.
  155.                  if (!gDBView.db)
  156.                    gRerootOnFolderLoad = true;
  157.                }
  158.                if (gRerootOnFolderLoad)
  159.                  RerootFolder(uri, msgFolder, gCurrentLoadingFolderViewType, gCurrentLoadingFolderViewFlags, gCurrentLoadingFolderSortType, gCurrentLoadingFolderSortOrder);
  160.  
  161.                if (gDBView) 
  162.                  gDBView.suppressCommandUpdating = false;
  163.  
  164.                gIsEditableMsgFolder = IsSpecialFolder(msgFolder, MSG_FOLDER_FLAG_DRAFTS, true);
  165.  
  166.                gCurrentLoadingFolderSortType = 0;
  167.                gCurrentLoadingFolderSortOrder = 0;
  168.                gCurrentLoadingFolderViewType = 0;
  169.                gCurrentLoadingFolderViewFlags = 0;
  170.  
  171.                var scrolled = false;
  172.  
  173.                LoadCurrentlyDisplayedMessage();  //used for rename folder msg loading after folder is loaded.
  174.  
  175.                if (gStartMsgKey != nsMsgKey_None) {
  176.                  scrolled = SelectAndScrollToKey(gStartMsgKey);
  177.                  gStartMsgKey = nsMsgKey_None;
  178.                }
  179.  
  180.                if (gNextMessageAfterLoad) {
  181.                  var type = gNextMessageAfterLoad;
  182.                  gNextMessageAfterLoad = null;
  183.  
  184.                  // scroll to and select the proper message
  185.                  scrolled = ScrollToMessage(type, true, true /* selectMessage */);
  186.                }
  187.              }
  188.            }
  189.            if (uri == gCurrentLoadingFolderURI) {
  190.              // NOTE,
  191.              // if you change the scrolling code below,
  192.              // double check the scrolling logic in
  193.              // searchBar.js, restorePreSearchView()
  194.              viewDebug("uri == current loading folder uri\n");
  195.              gCurrentLoadingFolderURI = "";
  196.  
  197.              // if we didn't just scroll, 
  198.              // scroll to the first new message
  199.              // but don't select it
  200.              if (!scrolled && pref.getBoolPref("mailnews.scroll_to_new_message"))
  201.                scrolled = ScrollToMessage(nsMsgNavigationType.firstNew, true, false /* selectMessage */);
  202.  
  203.              if (!scrolled && pref.getBoolPref("mailnews.remember_selected_message")) {
  204.                // if we failed to scroll to a new message,
  205.                // reselect the last selected message
  206.                var lastMessageLoaded = msgFolder.lastMessageLoaded;
  207.                if (lastMessageLoaded != nsMsgKey_None) {
  208.                  scrolled = SelectAndScrollToKey(lastMessageLoaded);
  209.                }
  210.              }
  211.  
  212.              if (!scrolled) {
  213.                // if we still haven't scrolled,
  214.                // scroll to the newest, which might be the top or the bottom
  215.                // depending on our sort order and sort type
  216.                if (gDBView.sortOrder == nsMsgViewSortOrder.ascending) {
  217.                  switch (gDBView.sortType) {
  218.                    case nsMsgViewSortType.byDate: 
  219.                    case nsMsgViewSortType.byId: 
  220.                    case nsMsgViewSortType.byThread: 
  221.                      scrolled = ScrollToMessage(nsMsgNavigationType.lastMessage, true, false /* selectMessage */);
  222.                      break;
  223.                  }
  224.                }
  225.  
  226.                // if still we haven't scrolled,
  227.                // scroll to the top.
  228.                if (!scrolled)
  229.                  EnsureRowInThreadTreeIsVisible(0);
  230.              }            
  231.  
  232.              // NOTE,
  233.              // if you change the scrolling code above,
  234.              // double check the scrolling logic in
  235.              // searchBar.js, restorePreSearchView()
  236.  
  237.              SetBusyCursor(window, false);
  238.            }
  239.            if (gNotifyDefaultInboxLoadedOnStartup && (folder.flags & 0x1000))
  240.            {
  241.               var defaultAccount = accountManager.defaultAccount;
  242.               defaultServer = defaultAccount.incomingServer;
  243.               var inboxFolder = GetInboxFolder(defaultServer);
  244.               if (inboxFolder && inboxFolder.URI == folder.URI)
  245.               {
  246.                 NotifyObservers(null,"defaultInboxLoadedOnStartup",null);
  247.                 gNotifyDefaultInboxLoadedOnStartup = false;
  248.               }
  249.            }
  250.            //folder loading is over, now issue quick search if there is an email address
  251.            viewDebug("in folder loaded gVirtualFolderTerms = " + gVirtualFolderTerms + "\n");
  252.            viewDebug("in folder loaded gMsgFolderSelected = " + gMsgFolderSelected.URI + "\n");
  253.            if (rerootingFolder)
  254.            {
  255.              if (gSearchEmailAddress)
  256.              {
  257.                Search(gSearchEmailAddress);
  258.                gSearchEmailAddress = null;
  259.              } 
  260.              else if (gVirtualFolderTerms)
  261.              {
  262.                 gDefaultSearchViewTerms = null;
  263.                 viewDebug("searching gVirtualFolderTerms\n");
  264.                 loadVirtualFolder();
  265.              }
  266.              else if (gMsgFolderSelected.flags & MSG_FOLDER_FLAG_VIRTUAL)
  267.              {
  268.                 viewDebug("selected folder is virtual\n");
  269.                 gDefaultSearchViewTerms = null;
  270.              }
  271.              else
  272.              {               
  273.                // get the view value from the folder
  274.                if (msgFolder)
  275.                {
  276.                  var msgDatabase = msgFolder.getMsgDatabase(msgWindow);
  277.                  var dbFolderInfo = msgDatabase.dBFolderInfo; 
  278.                  var result = dbFolderInfo.getUint32Property("current-view", 0);
  279.                  
  280.                  // if our new view is the same as the old view and we already have the list of search terms built up
  281.                  // for the old view, just re-use it
  282.                  if (gCurrentViewValue == result && gDefaultSearchViewTerms)
  283.                  {
  284.                    viewDebug("searching gDefaultSearchViewTerms and rerootingFolder\n");
  285.                    Search("");
  286.                  }
  287.                  else
  288.                  {
  289.                    viewDebug("changing view by value\n");
  290.                    ViewChangeByValue(result);
  291.                  }
  292.                }
  293.              }
  294.            }
  295.          }
  296.        } 
  297.        else if (eventType == "ImapHdrDownloaded") {
  298.          if (folder) {
  299.            var imapFolder = folder.QueryInterface(Components.interfaces.nsIMsgImapMailFolder);
  300.            if (imapFolder) {
  301.             var hdrParser = imapFolder.hdrParser;
  302.             if (hdrParser) {
  303.               var msgHdr = hdrParser.GetNewMsgHdr();
  304.               if (msgHdr)
  305.               {
  306.                 var hdrs = hdrParser.headers;
  307.                 if (hdrs && hdrs.indexOf("X-attachment-size:") > 0) {
  308.                   msgHdr.OrFlags(0x10000000);  // 0x10000000 is MSG_FLAG_ATTACHMENT
  309.                 }
  310.                 if (hdrs && hdrs.indexOf("X-image-size:") > 0) {
  311.                   msgHdr.setStringProperty("imageSize", "1");
  312.                 }
  313.               }
  314.             }
  315.            }
  316.          }
  317.        }
  318.        else if (eventType == "DeleteOrMoveMsgCompleted") {
  319.          HandleDeleteOrMoveMsgCompleted(folder);
  320.        }     
  321.        else if (eventType == "DeleteOrMoveMsgFailed") {
  322.          HandleDeleteOrMoveMsgFailed(folder);
  323.        }
  324.        else if (eventType == "CompactCompleted") {
  325.          HandleCompactCompleted(folder);
  326.        }
  327.        else if (eventType == "RenameCompleted") {
  328.          SelectFolder(folder.URI);
  329.        }
  330.        else if (eventType == "JunkStatusChanged") {
  331.          HandleJunkStatusChanged(folder);
  332.        }
  333.     }
  334. }
  335.  
  336. var folderObserver = {
  337.     canDrop: function(index, orientation)
  338.     {
  339.         return CanDropOnFolderTree(index, orientation);
  340.     },
  341.  
  342.     onDrop: function(row, orientation)
  343.     {
  344.         DropOnFolderTree(row, orientation);
  345.     },
  346.  
  347.     onToggleOpenState: function()
  348.     {
  349.     },
  350.  
  351.     onCycleHeader: function(colID, elt)
  352.     {
  353.     },
  354.  
  355.     onCycleCell: function(row, colID)
  356.     {
  357.     },
  358.  
  359.     onSelectionChanged: function()
  360.     {
  361.     },
  362.  
  363.     onPerformAction: function(action)
  364.     {
  365.     },
  366.  
  367.     onPerformActionOnRow: function(action, row)
  368.     {
  369.     },
  370.  
  371.     onPerformActionOnCell: function(action, row, colID)
  372.     {
  373.     }
  374. }
  375.  
  376. function HandleDeleteOrMoveMsgFailed(folder)
  377. {
  378.   gDBView.onDeleteCompleted(false);
  379.   if(IsCurrentLoadedFolder(folder)) {
  380.     if(gNextMessageAfterDelete) {
  381.       gNextMessageAfterDelete = null;
  382.       gNextMessageViewIndexAfterDelete = -2;
  383.     }
  384.   }
  385.  
  386.   // fix me???
  387.   // ThreadPaneSelectionChange(true);
  388. }
  389.  
  390. // WARNING
  391. // this is a fragile and complicated function.
  392. // be careful when hacking on it.
  393. // don't forget about things like different imap 
  394. // delete models, multiple views (from multiple thread panes, 
  395. // search windows, stand alone message windows)
  396. function HandleDeleteOrMoveMsgCompleted(folder)
  397. {
  398.   // you might not have a db view.  this can happen if
  399.   // biff fires when the 3 pane is set to account central.
  400.   if (!gDBView)
  401.     return;
  402.  
  403.   gDBView.onDeleteCompleted(true);
  404.  
  405.   if (!IsCurrentLoadedFolder(folder)) {
  406.     // default value after delete/move/copy is over
  407.     gNextMessageViewIndexAfterDelete = -2;
  408.     return;
  409.   }
  410.  
  411.   var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
  412.   var treeSelection = treeView.selection;
  413.           
  414.   if (gNextMessageViewIndexAfterDelete == -2) {
  415.     // a move or delete can cause our selection can change underneath us.
  416.     // this can happen when the user
  417.     // deletes message from the stand alone msg window
  418.     // or the search view, or another 3 pane
  419.     if (treeSelection.count == 0) {
  420.       // this can happen if you double clicked a message
  421.       // in the thread pane, and deleted it from the stand alone msg window
  422.       // see bug #172392
  423.       treeSelection.clearSelection();
  424.       setTitleFromFolder(folder, null);
  425.       ClearMessagePane();
  426.       UpdateMailToolbar("delete from another view, 0 rows now selected");
  427.     }
  428.     else if (treeSelection.count == 1) {
  429.       // this can happen if you had two messages selected
  430.       // in the thread pane, and you deleted one of them from another view
  431.       // (like the view in the stand alone msg window)
  432.       // since one item is selected, we should load it.
  433.       var startIndex = {};
  434.       var endIndex = {};
  435.       treeSelection.getRangeAt(0, startIndex, endIndex);
  436.         
  437.       // select the selected item, so we'll load it
  438.       treeSelection.select(startIndex.value); 
  439.       treeView.selectionChanged();
  440.  
  441.       EnsureRowInThreadTreeIsVisible(startIndex.value); 
  442.  
  443.       UpdateMailToolbar("delete from another view, 1 row now selected");
  444.     }
  445.     else {
  446.       // this can happen if you have more than 2 messages selected
  447.       // in the thread pane, and you deleted one of them from another view
  448.       // (like the view in the stand alone msg window)
  449.       // since multiple messages are still selected, do nothing.
  450.     }
  451.   }
  452.   else {
  453.     if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None) 
  454.     {
  455.       var viewSize = treeView.rowCount;
  456.       if (gNextMessageViewIndexAfterDelete >= viewSize) 
  457.       {
  458.         if (viewSize > 0)
  459.           gNextMessageViewIndexAfterDelete = viewSize - 1;
  460.         else
  461.         {           
  462.           gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None;
  463.  
  464.           // there is nothing to select since viewSize is 0
  465.           treeSelection.clearSelection();
  466.           setTitleFromFolder(folder, null);
  467.           ClearMessagePane();
  468.           UpdateMailToolbar("delete from current view, 0 rows left");
  469.         }
  470.       }
  471.     }
  472.  
  473.     // if we are about to set the selection with a new element then DON'T clear
  474.     // the selection then add the next message to select. This just generates
  475.     // an extra round of command updating notifications that we are trying to
  476.     // optimize away.
  477.     if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None) 
  478.     {
  479.       // when deleting a message we don't update the commands 
  480.       // when the selection goes to 0
  481.       // (we have a hack in nsMsgDBView which prevents that update) 
  482.       // so there is no need to
  483.       // update commands when we select the next message after the delete; 
  484.       // the commands already
  485.       // have the right update state...
  486.       gDBView.suppressCommandUpdating = true;
  487.  
  488.       // This check makes sure that the tree does not perform a
  489.       // selection on a non selected row (row < 0), else assertions will
  490.       // be thrown.
  491.       if (gNextMessageViewIndexAfterDelete >= 0)
  492.         treeSelection.select(gNextMessageViewIndexAfterDelete);
  493.         
  494.       // if gNextMessageViewIndexAfterDelete has the same value 
  495.       // as the last index we had selected, the tree won't generate a
  496.       // selectionChanged notification for the tree view. So force a manual
  497.       // selection changed call. 
  498.       // (don't worry it's cheap if we end up calling it twice).
  499.       if (treeView)
  500.         treeView.selectionChanged();
  501.  
  502.       EnsureRowInThreadTreeIsVisible(gNextMessageViewIndexAfterDelete); 
  503.       gDBView.suppressCommandUpdating = false;
  504.  
  505.       // hook for extra toolbar items
  506.       // XXX TODO
  507.       // I think there is a bug in the suppression code above.
  508.       // what if I have two rows selected, and I hit delete, 
  509.       // and so we load the next row.
  510.       // what if I have commands that only enable where 
  511.       // exactly one row is selected?
  512.       UpdateMailToolbar("delete from current view, at least one row selected");
  513.     }
  514.   }
  515.  
  516.   // default value after delete/move/copy is over
  517.   gNextMessageViewIndexAfterDelete = -2;
  518. }
  519.  
  520. function HandleCompactCompleted(folder)
  521. {
  522.   if (folder)
  523.   {
  524.     var resource = folder.QueryInterface(Components.interfaces.nsIRDFResource);
  525.     if (resource)
  526.     {
  527.       var uri = resource.Value;
  528.       var msgFolder = msgWindow.openFolder;
  529.       if (msgFolder && uri == msgFolder.URI)
  530.       {
  531.         var msgdb = msgFolder.getMsgDatabase(msgWindow);
  532.         if (msgdb)
  533.         {
  534.           var dbFolderInfo = msgdb.dBFolderInfo;
  535.           sortType = dbFolderInfo.sortType;
  536.           sortOrder = dbFolderInfo.sortOrder;
  537.           viewFlags = dbFolderInfo.viewFlags;
  538.           viewType = dbFolderInfo.viewType;
  539.           dbFolderInfo = null;
  540.         }
  541.         RerootFolder(uri, msgFolder, viewType, viewFlags, sortType, sortOrder);
  542.         LoadCurrentlyDisplayedMessage();
  543.       }
  544.     }
  545.   }
  546. }
  547.  
  548. function LoadCurrentlyDisplayedMessage()
  549. {
  550.   if (gCurrentlyDisplayedMessage != nsMsgViewIndex_None)
  551.   {
  552.     var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
  553.     var treeSelection = treeView.selection;
  554.     treeSelection.select(gCurrentlyDisplayedMessage);
  555.     if (treeView)
  556.       treeView.selectionChanged();
  557.     EnsureRowInThreadTreeIsVisible(gCurrentlyDisplayedMessage);
  558.     SetFocusThreadPane();
  559.     gCurrentlyDisplayedMessage = nsMsgViewIndex_None; //reset
  560.   }
  561. }
  562.  
  563. function IsCurrentLoadedFolder(folder)
  564. {
  565.   var msgfolder = folder.QueryInterface(Components.interfaces.nsIMsgFolder);
  566.   if(msgfolder)
  567.   {
  568.     var folderResource = msgfolder.QueryInterface(Components.interfaces.nsIRDFResource);
  569.     if(folderResource)
  570.     {
  571.       var folderURI = folderResource.Value;
  572.       var currentLoadedFolder = GetThreadPaneFolder();
  573.       if (currentLoadedFolder.flags & MSG_FOLDER_FLAG_VIRTUAL)
  574.       {
  575.         var msgDatabase = currentLoadedFolder.getMsgDatabase(msgWindow);
  576.         var dbFolderInfo = msgDatabase.dBFolderInfo;
  577.         var srchFolderUri = dbFolderInfo.getCharPtrProperty("searchFolderUri");
  578.         var re = new RegExp("^" + folderURI + "$|^" + folderURI + "\||\|" + folderURI + "$|\|" + folderURI +"\|");
  579.         var retval = (currentLoadedFolder.URI.match(re));
  580.         return retval;
  581.  
  582.       }
  583.       var currentURI = currentLoadedFolder.URI;
  584.       return(currentURI == folderURI);
  585.     }
  586.   }
  587.  
  588.   return false;
  589. }
  590.  
  591. function ServerContainsFolder(server, folder)
  592. {
  593.   if (!folder || !server)
  594.     return false;
  595.  
  596.   return server.equals(folder.server);
  597. }
  598.  
  599. function SelectServer(server)
  600. {
  601.   SelectFolder(server.rootFolder.URI);
  602. }
  603.  
  604. // we have this incoming server listener in case we need to
  605. // alter the folder pane selection when a server is removed
  606. // or changed (currently, when the real username or real hostname change)
  607. var gThreePaneIncomingServerListener = {
  608.     onServerLoaded: function(server) {},
  609.     onServerUnloaded: function(server) {
  610.       var selectedFolders = GetSelectedMsgFolders();
  611.       for (var i = 0; i < selectedFolders.length; i++) {
  612.         if (ServerContainsFolder(server, selectedFolders[i])) {
  613.           SelectServer(accountManager.defaultAccount.incomingServer);
  614.           // we've made a new selection, we're done
  615.           return;
  616.         }
  617.       }
  618.    
  619.       // if nothing is selected at this point, better go select the default
  620.       // this could happen if nothing was selected when the server was removed
  621.       selectedFolders = GetSelectedMsgFolders();
  622.       if (selectedFolders.length == 0) {
  623.         SelectServer(accountManager.defaultAccount.incomingServer);
  624.       }
  625.     },
  626.     onServerChanged: function(server) {
  627.       // if the current selected folder is on the server that changed
  628.       // and that server is an imap or news server, 
  629.       // we need to update the selection. 
  630.       // on those server types, we'll be reconnecting to the server
  631.       // and our currently selected folder will need to be reloaded
  632.       // or worse, be invalid.
  633.       if (server.type != "imap" && server.type !="nntp")
  634.         return;
  635.  
  636.       var selectedFolders = GetSelectedMsgFolders();
  637.       for (var i = 0; i < selectedFolders.length; i++) {
  638.         // if the selected item is a server, we don't have to update
  639.         // the selection
  640.         if (!(selectedFolders[i].isServer) && ServerContainsFolder(server, selectedFolders[i])) {
  641.           SelectServer(server);
  642.           // we've made a new selection, we're done
  643.           return;
  644.         }
  645.       }
  646.     }
  647. }
  648.  
  649. function GetMailPaneConfig() {
  650.   try {
  651.     return pref.getIntPref("mail.pane_config.dynamic");
  652.   } catch (e) {
  653.     return pref.getIntPref("mail.pane_config");
  654.   }
  655. }
  656.  
  657. function UpdateMailPaneConfig() {
  658.   const dynamicIds = ["messagesBox", "mailContent", "threadPaneBox"]
  659.   var desiredId = dynamicIds[GetMailPaneConfig()];
  660.   var messagePane = GetMessagePane();
  661.   if (messagePane.parentNode.id != desiredId) {
  662.     ClearAttachmentList();
  663.     var messagePaneSplitter = GetThreadAndMessagePaneSplitter();
  664.     var desiredParent = document.getElementById(desiredId);
  665.     desiredParent.appendChild(messagePaneSplitter);
  666.     desiredParent.appendChild(messagePane);
  667.     messagePaneSplitter.orient = desiredParent.orient;
  668.     messenger.SetWindow(null, null);
  669.     messenger.SetWindow(window, msgWindow);
  670.     if (gDBView && GetNumSelectedMessages() == 1)
  671.       gDBView.reloadMessage();
  672.   }
  673. }
  674.  
  675. const MailPaneConfigObserver = {
  676.   observe: function observe(subject, topic, prefName) {
  677.     // verify that we're changing the mail pane config pref
  678.     if (topic == "nsPref:changed")
  679.       UpdateMailPaneConfig();
  680.   }
  681. };
  682.  
  683. /* Functions related to startup */
  684. function OnLoadMessenger()
  685. {
  686.   AddMailOfflineObserver();
  687.   CreateMailWindowGlobals();
  688.   pref.QueryInterface(Components.interfaces.nsIPrefBranch2);
  689.   pref.addObserver("mail.pane_config.dynamic", MailPaneConfigObserver, false);
  690.   UpdateMailPaneConfig();
  691.   Create3PaneGlobals();
  692.   AddToolBarPrefListener();
  693.   ShowHideToolBarButtons();
  694.   verifyAccounts(null);
  695.     
  696.   HideAccountCentral();
  697.   InitMsgWindow();
  698.   messenger.SetWindow(window, msgWindow);
  699.  
  700.   InitializeDataSources();
  701.   InitPanes();
  702.  
  703.   accountManager.setSpecialFolders();
  704.   accountManager.loadVirtualFolders();
  705.   accountManager.addIncomingServerListener(gThreePaneIncomingServerListener);
  706.  
  707.   AddToSession();
  708.   //need to add to session before trying to load start folder otherwise listeners aren't
  709.   //set up correctly.
  710.   // argument[0] --> folder uri
  711.   // argument[1] --> optional message key
  712.   // argument[2] --> optional email address; //will come from aim; needs to show msgs from buddy's email address  
  713.   if ("arguments" in window)
  714.   {
  715.     gStartFolderUri = (window.arguments.length > 0) ? window.arguments[0]
  716.                                                     : null;
  717.     gStartMsgKey = (window.arguments.length > 1) ? window.arguments[1]
  718.                                                  : nsMsgKey_None;
  719.     gSearchEmailAddress = (window.arguments.length > 2) ? window.arguments[2]
  720.                                                         : null;
  721.   }
  722.  
  723.   setTimeout("loadStartFolder(gStartFolderUri);", 0);
  724.  
  725.   // FIX ME - later we will be able to use onload from the overlay
  726.   OnLoadMsgHeaderPane();
  727.  
  728.   gHaveLoadedMessage = false;
  729.  
  730.   gNotifyDefaultInboxLoadedOnStartup = true;
  731.  
  732.   //Set focus to the Thread Pane the first time the window is opened.
  733.   SetFocusThreadPane();
  734. }
  735.  
  736. function OnUnloadMessenger()
  737. {
  738.   pref.removeObserver("mail.pane_config.dynamic", MailPaneConfigObserver, false);
  739.  
  740.   OnLeavingFolder(gMsgFolderSelected);  // mark all read in current folder
  741.   accountManager.removeIncomingServerListener(gThreePaneIncomingServerListener);
  742.   RemoveToolBarPrefListener();
  743.   // FIX ME - later we will be able to use onload from the overlay
  744.   OnUnloadMsgHeaderPane();
  745.  
  746.   OnMailWindowUnload();
  747. }
  748.  
  749. function NotifyObservers(aSubject, aTopic, aData)
  750. {
  751.   var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
  752.   observerService.notifyObservers(aSubject, aTopic, aData);
  753. }
  754.  
  755.  
  756. function Create3PaneGlobals()
  757. {
  758. }
  759.  
  760. // because the "open" state persists, we'll call
  761. // PerformExpand() for all servers that are open at startup.            
  762. function PerformExpandForAllOpenServers()
  763. {
  764.     var servers = accountManager.allServers;
  765.     for (var i = 0; i < servers.Count(); i++)
  766.     {
  767.         var server = servers.QueryElementAt(i, Components.interfaces.nsIMsgIncomingServer);
  768.         if (server.type != "imap" && !server.rootMsgFolder.getFlag(MSG_FOLDER_FLAG_ELIDED))
  769.             server.performExpand(msgWindow);
  770.     }
  771. }
  772.  
  773. function loadStartFolder(initialUri)
  774. {
  775.     var folderTree = GetFolderTree();
  776.     var defaultServer = null;
  777.     var startFolderResource = null;
  778.     var isLoginAtStartUpEnabled = false;
  779.  
  780.     //First get default account
  781.     try
  782.     {
  783.         if(initialUri)
  784.             startFolderResource = RDF.GetResource(initialUri);
  785.         else
  786.         {
  787.             var defaultAccount = accountManager.defaultAccount;
  788.  
  789.             defaultServer = defaultAccount.incomingServer;
  790.             var rootMsgFolder = defaultServer.rootMsgFolder;
  791.  
  792.             startFolderResource = rootMsgFolder.QueryInterface(Components.interfaces.nsIRDFResource);
  793.  
  794.             // Enable checknew mail once by turning checkmail pref 'on' to bring 
  795.             // all users to one plane. This allows all users to go to Inbox. User can 
  796.             // always go to server settings panel and turn off "Check for new mail at startup"
  797.             if (!pref.getBoolPref(kMailCheckOncePrefName))
  798.             {
  799.                 pref.setBoolPref(kMailCheckOncePrefName, true);
  800.                 defaultServer.loginAtStartUp = true;
  801.             }
  802.  
  803.             // Get the user pref to see if the login at startup is enabled for default account
  804.             isLoginAtStartUpEnabled = defaultServer.loginAtStartUp;
  805.  
  806.             // Get Inbox only if when we have to login 
  807.             if (isLoginAtStartUpEnabled) 
  808.             {
  809.                 //now find Inbox
  810.                 var outNumFolders = new Object();
  811.                 var inboxFolder = rootMsgFolder.getFoldersWithFlag(0x1000, 1, outNumFolders); 
  812.                 if (!inboxFolder) return;
  813.  
  814.                 startFolderResource = inboxFolder.QueryInterface(Components.interfaces.nsIRDFResource);
  815.             }
  816.             else
  817.             {
  818.                 // set the startFolderResource to the server, so we select it
  819.                 // so we'll get account central
  820.                 startFolderResource = RDF.GetResource(defaultServer.serverURI);
  821.             }
  822.         }
  823.  
  824.         var startFolder = startFolderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
  825.  
  826.         // Perform biff on the server to check for new mail, except for 
  827.         // a pop3 account that is deferred or deferred to,
  828.         // or the case where initialUri is non-null (non-startup)
  829.         if (!initialUri && isLoginAtStartUpEnabled && gLoadStartFolder
  830.             && !defaultServer.isDeferredTo &&
  831.             defaultServer.rootFolder == defaultServer.rootMsgFolder)
  832.           defaultServer.PerformBiff(msgWindow);        
  833.  
  834.         SelectFolder(startFolder.URI);
  835.  
  836.         // because the "open" state persists, we'll call
  837.         // PerformExpand() for all servers that are open at startup.
  838.         // note, because of the "news.persist_server_open_state_in_folderpane" pref
  839.         // we don't persist the "open" state of news servers across sessions, 
  840.         // but we do within a session, so if you open another 3 pane
  841.         // and a news server is "open", we'll update the unread counts.
  842.         PerformExpandForAllOpenServers();
  843.     }
  844.     catch(ex)
  845.     {
  846.         dump(ex);
  847.         dump('Exception in LoadStartFolder caused by no default account.  We know about this\n');
  848.     }
  849.  
  850.     MsgGetMessagesForAllServers(defaultServer);
  851.  
  852.     if (CheckForUnsentMessages())
  853.     {
  854.         var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  855.         if (!ioService.offline) 
  856.         {
  857.             InitPrompts();
  858.             InitServices();
  859.  
  860.             if (gPromptService) 
  861.             {
  862.                 var buttonPressed = gPromptService.confirmEx(window, 
  863.                                     gOfflinePromptsBundle.getString('sendMessagesOfflineWindowTitle'), 
  864.                                     gOfflinePromptsBundle.getString('sendMessagesLabel'),
  865.                                     gPromptService.BUTTON_TITLE_IS_STRING * (gPromptService.BUTTON_POS_0 + 
  866.                                         gPromptService.BUTTON_POS_1),
  867.                                     gOfflinePromptsBundle.getString('sendMessagesSendButtonLabel'),
  868.                                     gOfflinePromptsBundle.getString('sendMessagesNoSendButtonLabel'),
  869.                                     null, null, {value:0});
  870.                 if (buttonPressed == 0) 
  871.                     SendUnsentMessages();
  872.             }
  873.         }
  874.     }
  875. }
  876.  
  877. function AddToSession()
  878. {
  879.     try {
  880.         var mailSession = Components.classes[mailSessionContractID].getService(Components.interfaces.nsIMsgMailSession);
  881.         
  882.         var nsIFolderListener = Components.interfaces.nsIFolderListener;
  883.         var notifyFlags = nsIFolderListener.intPropertyChanged | nsIFolderListener.event;
  884.         mailSession.AddFolderListener(folderListener, notifyFlags);
  885.     } catch (ex) {
  886.         dump("Error adding to session\n");
  887.     }
  888. }
  889.  
  890. function InitPanes()
  891. {
  892.     OnLoadFolderPane();
  893.     OnLoadLocationTree();
  894.     OnLoadThreadPane();
  895.     SetupCommandUpdateHandlers();
  896. }
  897.  
  898. function InitializeDataSources()
  899. {
  900.     //Setup common mailwindow stuff.
  901.     AddDataSources();
  902.  
  903.     SetupMoveCopyMenus('goMenu', accountManagerDataSource, folderDataSource);
  904.  
  905.     //To threadpane move context menu
  906.     SetupMoveCopyMenus('threadPaneContext-moveMenu', accountManagerDataSource, folderDataSource);
  907.  
  908.     //To threadpane copy content menu
  909.     SetupMoveCopyMenus('threadPaneContext-copyMenu', accountManagerDataSource, folderDataSource);
  910. }
  911.  
  912. function OnFolderUnreadColAttrModified(event)
  913. {
  914.     if (event.attrName == "hidden")
  915.     {
  916.         var folderNameCell = document.getElementById("folderNameCell");
  917.         var label = {"true": "?folderTreeName", "false": "?folderTreeSimpleName"};
  918.         folderNameCell.setAttribute("label", label[event.newValue]);
  919.     }
  920. }
  921.  
  922. function OnAttachmentColAttrModified(event)
  923. {
  924.   if (event.attrName == "hidden")
  925.     UpdateAttachmentCol(false);
  926. }
  927.  
  928. function UpgradeFolderPaneUI()
  929. {
  930.   // placeholder in case any new columns get added to the folder pane
  931.   // note that this function fails to notice a pane layout switch
  932.   // var folderPaneUIVersion = pref.getIntPref("mail.ui.folderpane.version");
  933. }
  934.  
  935. function OnLoadFolderPane()
  936. {
  937.     UpgradeFolderPaneUI();
  938.  
  939.     var folderUnreadCol = document.getElementById("folderUnreadCol");
  940.     var hidden = folderUnreadCol.getAttribute("hidden");
  941.     if (hidden != "true")
  942.     {
  943.         var folderNameCell = document.getElementById("folderNameCell");
  944.         folderNameCell.setAttribute("label", "?folderTreeSimpleName");
  945.     }
  946.     folderUnreadCol.addEventListener("DOMAttrModified", OnFolderUnreadColAttrModified, false);
  947.  
  948.     //Add folderDataSource and accountManagerDataSource to folderPane
  949.     accountManagerDataSource = accountManagerDataSource.QueryInterface(Components.interfaces.nsIRDFDataSource);
  950.     folderDataSource = folderDataSource.QueryInterface(Components.interfaces.nsIRDFDataSource);
  951.     var database = GetFolderDatasource();
  952.  
  953.     database.AddDataSource(accountManagerDataSource);
  954.     database.AddDataSource(folderDataSource);
  955.     var folderTree = GetFolderTree();
  956.     folderTree.setAttribute("ref", "msgaccounts:/");
  957.  
  958.     var folderTreeBuilder = folderTree.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder);
  959.     folderTreeBuilder.addObserver(folderObserver);
  960.     folderTree.addEventListener("click",FolderPaneOnClick,true);
  961.     folderTree.addEventListener("mousedown",TreeOnMouseDown,true);
  962. }
  963.  
  964. // builds prior to 12-08-2001 did not have the labels column
  965. // in the thread pane.  so if a user ran an old build, and then
  966. // upgraded, they get the new column, and this causes problems.
  967. // We're trying to avoid a similar problem to bug #96979.
  968. // to work around this, we hide the column once, using the 
  969. // "mailnews.ui.threadpane.version" pref.
  970. function UpgradeThreadPaneUI()
  971. {
  972.   try {
  973.     var threadTree = document.getElementById("threadTree");
  974.     var junkCol = document.getElementById("junkStatusCol");
  975.     var subjectCol = document.getElementById("subjectCol");
  976.     var beforeCol;
  977.     switch (pref.getIntPref("mailnews.ui.threadpane.version")) {
  978.       case 1: // upgrade from 1 to 2
  979.       case 2: // upgrade from 2 to 3
  980.         beforeCol = subjectCol.boxObject.nextSibling.boxObject.nextSibling;
  981.         if (beforeCol)
  982.           threadTree._reorderColumn(junkCol, beforeCol, true);
  983.         else // subjectCol was the last column, put it after
  984.           threadTree._reorderColumn(junkCol, subjectCol, false);
  985.  
  986.       case 3: // upgrade from 3 to 4
  987.         var senderCol = document.getElementById("senderCol");
  988.         var recipientCol = document.getElementById("recipientCol");
  989.         beforeCol = junkCol.boxObject.nextSibling.boxObject.nextSibling;
  990.         if (beforeCol)
  991.           threadTree._reorderColumn(recipientCol, beforeCol, true);
  992.         else // junkCol was the last column, put it after
  993.           threadTree._reorderColumn(recipientCol, junkCol, false);
  994.         threadTree._reorderColumn(senderCol, recipientCol, true);
  995.  
  996.       case 4: // upgrade from 4 to 5
  997.         var attachmentCol = document.getElementById("attachmentCol");
  998.         threadTree._reorderColumn(attachmentCol, subjectCol, true);
  999.         pref.setIntPref("mailnews.ui.threadpane.version", 5);
  1000.  
  1001.       default: // already upgraded
  1002.         break;
  1003.     }
  1004.   }
  1005.   catch (ex) {
  1006.     dump("UpgradeThreadPane: ex = " + ex + "\n");
  1007.   }
  1008. }
  1009.  
  1010. function OnLoadThreadPane()
  1011. {
  1012.     UpgradeThreadPaneUI();
  1013.     UpdateAttachmentCol(true);
  1014. }
  1015.  
  1016. function UpdateAttachmentCol(aFirstTimeFlag)
  1017. {
  1018.   var attachmentCol = document.getElementById("attachmentCol");
  1019.   var threadTree = GetThreadTree();
  1020.   threadTree.setAttribute("noattachcol", attachmentCol.getAttribute("hidden"));
  1021.   if (aFirstTimeFlag)
  1022.     attachmentCol.addEventListener("DOMAttrModified", OnAttachmentColAttrModified, false);
  1023.   else
  1024.     threadTree.treeBoxObject.clearStyleAndImageCaches();
  1025. }
  1026.  
  1027. function OnLocationToolbarAttrModified(event)
  1028. {
  1029.     if (!/collapsed/.test(event.attrName))
  1030.         return;
  1031.     var searchBox = document.getElementById("searchBox");
  1032.     var desiredParent = document.getElementById("msgLocationToolbar");
  1033.     if (desiredParent.getAttribute("collapsed") == "true" ||
  1034.         desiredParent.getAttribute("moz-collapsed") == "true")
  1035.         desiredParent = document.getElementById("searchBoxHolder");
  1036.     if (searchBox.parentNode != desiredParent)
  1037.         desiredParent.appendChild(searchBox);
  1038. }
  1039.  
  1040. function OnLoadLocationTree()
  1041. {
  1042.     var locationTree = document.getElementById('locationPopup').tree;
  1043.     locationTree.database.AddDataSource(accountManagerDataSource);
  1044.     locationTree.database.AddDataSource(folderDataSource);
  1045.     locationTree.setAttribute("ref", "msgaccounts:/");
  1046.     var toolbar = document.getElementById("msgLocationToolbar");
  1047.     toolbar.addEventListener("DOMAttrModified", OnLocationToolbarAttrModified, false);
  1048.     OnLocationToolbarAttrModified({attrName:"collapsed"});
  1049. }
  1050.  
  1051. function OnLocationTreeSelect(menulist)
  1052. {
  1053.     SelectFolder(menulist.getAttribute('uri'));
  1054. }
  1055.  
  1056. function UpdateLocationBar(resource)
  1057. {
  1058.     var tree = GetFolderTree();
  1059.     var folders = document.getElementById('locationFolders');
  1060.     var icon = document.getElementById('locationIcon');
  1061.     var names = ['BiffState', 'NewMessages', 'HasUnreadMessages',
  1062.         'SpecialFolder', 'IsServer', 'IsSecure', 'ServerType', 'NoSelect'];
  1063.     var label = GetFolderAttribute(tree, resource, 'FolderTreeName');
  1064.     folders.setAttribute("label", label);
  1065.     for (var i in names) {
  1066.         var name = names[i];
  1067.         var value = GetFolderAttribute(tree, resource, name);
  1068.         folders.setAttribute(name, value);
  1069.         icon.setAttribute(name, value);
  1070.     }
  1071.     folders.setAttribute('uri', resource.Value);
  1072. }
  1073.  
  1074. function goToggleLocationToolbar(toggle)
  1075. {
  1076.     // XXX hidden doesn't work, use collapsed instead
  1077.     var menu = document.getElementById("menu_showLocationToolbar");
  1078.     var toolbar = document.getElementById("msgLocationToolbar");
  1079.     var checked = toolbar.collapsed;
  1080.     menu.setAttribute('checked', checked);
  1081.     toolbar.setAttribute('collapsed', !checked);
  1082. }
  1083.  
  1084. function GetFolderDatasource()
  1085. {
  1086.     var folderTree = GetFolderTree();
  1087.     return folderTree.database;
  1088. }
  1089.  
  1090. /* Functions for accessing particular parts of the window*/
  1091. function GetFolderTree()
  1092. {
  1093.     if (! gFolderTree)
  1094.         gFolderTree = document.getElementById("folderTree");
  1095.     return gFolderTree;
  1096. }
  1097.  
  1098. function GetSearchInput()
  1099. {
  1100.     if (gSearchInput) return gSearchInput;
  1101.     gSearchInput = document.getElementById("searchInput");
  1102.     return gSearchInput;
  1103. }
  1104.  
  1105. function GetMessagePane()
  1106. {
  1107.     if (gMessagePane) return gMessagePane;
  1108.     gMessagePane = document.getElementById("messagepanebox");
  1109.     return gMessagePane;
  1110. }
  1111.  
  1112. function GetMessagePaneFrame()
  1113. {
  1114.     return window.content;
  1115. }
  1116.  
  1117. function FindInSidebar(currentWindow, id)
  1118. {
  1119.     var item = currentWindow.document.getElementById(id);
  1120.     if(item)
  1121.         return item;
  1122.  
  1123.     for(var i = 0; i < currentWindow.frames.length; i++)
  1124.     {
  1125.         var frameItem = FindInSidebar(currentWindow.frames[i], id);
  1126.         if(frameItem)
  1127.             return frameItem;
  1128.     }
  1129.  
  1130.     return null;
  1131. }
  1132.  
  1133. function GetThreadAndMessagePaneSplitter()
  1134. {
  1135.     if(gThreadAndMessagePaneSplitter) return gThreadAndMessagePaneSplitter;
  1136.     var splitter = document.getElementById('threadpane-splitter');
  1137.     gThreadAndMessagePaneSplitter = splitter;
  1138.     return splitter;
  1139. }
  1140.  
  1141. function GetUnreadCountElement()
  1142. {
  1143.     if(gUnreadCount) return gUnreadCount;
  1144.     var unreadCount = document.getElementById('unreadMessageCount');
  1145.     gUnreadCount = unreadCount;
  1146.     return unreadCount;
  1147. }
  1148. function GetTotalCountElement()
  1149. {
  1150.     if(gTotalCount) return gTotalCount;
  1151.     var totalCount = document.getElementById('totalMessageCount');
  1152.     gTotalCount = totalCount;
  1153.     return totalCount;
  1154. }
  1155.  
  1156. function IsMessagePaneCollapsed()
  1157. {
  1158.   return GetMessagePane().collapsed;
  1159. }
  1160.  
  1161. function IsFolderPaneCollapsed()
  1162. {
  1163.   return GetFolderTree().parentNode.collapsed;
  1164. }
  1165.  
  1166. function FindMessenger()
  1167. {
  1168.   return messenger;
  1169. }
  1170.  
  1171. function ClearThreadPaneSelection()
  1172. {
  1173.   try {
  1174.     if (gDBView) {
  1175.       var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
  1176.       var treeSelection = treeView.selection;
  1177.       if (treeSelection) 
  1178.         treeSelection.clearSelection(); 
  1179.     }
  1180.   }
  1181.   catch (ex) {
  1182.     dump("ClearThreadPaneSelection: ex = " + ex + "\n");
  1183.   }
  1184. }
  1185.  
  1186. function ClearMessagePane()
  1187. {
  1188.   if(gHaveLoadedMessage)
  1189.   {
  1190.     gHaveLoadedMessage = false;
  1191.     gCurrentDisplayedMessage = null;
  1192.     if (GetMessagePaneFrame().location.href != "about:blank")
  1193.         GetMessagePaneFrame().location.href = "about:blank";
  1194.  
  1195.     // hide the message header view AND the message pane...
  1196.     HideMessageHeaderPane();
  1197.     gMessageNotificationBar.clearMsgNotifications();
  1198.   }
  1199. }
  1200.  
  1201. function GetSelectedFolderIndex()
  1202. {
  1203.     var folderTree = GetFolderTree();
  1204.     var startIndex = {};
  1205.     var endIndex = {};
  1206.     folderTree.view.selection.getRangeAt(0, startIndex, endIndex);
  1207.     return startIndex.value;
  1208. }
  1209.  
  1210. // Function to change the highlighted row to where the mouse was clicked
  1211. // without loading the contents of the selected row.
  1212. // It will also keep the outline/dotted line in the original row.
  1213. function ChangeSelectionWithoutContentLoad(event, tree)
  1214. {
  1215.     var treeBoxObj = tree.treeBoxObject;
  1216.     var treeSelection = tree.view.selection;
  1217.  
  1218.     var row = treeBoxObj.getRowAt(event.clientX, event.clientY);
  1219.     // make sure that row.value is valid so that it doesn't mess up
  1220.     // the call to ensureRowIsVisible().
  1221.     if((row >= 0) && !treeSelection.isSelected(row))
  1222.     {
  1223.         var saveCurrentIndex = treeSelection.currentIndex;
  1224.         treeSelection.selectEventsSuppressed = true;
  1225.         treeSelection.select(row);
  1226.         treeSelection.currentIndex = saveCurrentIndex;
  1227.         treeBoxObj.ensureRowIsVisible(row);
  1228.         treeSelection.selectEventsSuppressed = false;
  1229.  
  1230.         // Keep track of which row in the thread pane is currently selected.
  1231.         if(tree.id == "threadTree")
  1232.           gThreadPaneCurrentSelectedIndex = row;
  1233.     }
  1234.     event.preventBubble();
  1235. }
  1236.  
  1237. function TreeOnMouseDown(event)
  1238. {
  1239.     // Detect right mouse click and change the highlight to the row
  1240.     // where the click happened without loading the message headers in
  1241.     // the Folder or Thread Pane.
  1242.     if (event.button == 2)
  1243.     {
  1244.       gRightMouseButtonDown = true;
  1245.       ChangeSelectionWithoutContentLoad(event, event.target.parentNode);
  1246.     }
  1247.     else
  1248.       gRightMouseButtonDown = false;
  1249. }
  1250.  
  1251. function FolderPaneOnClick(event)
  1252. {
  1253.     // we only care about button 0 (left click) events
  1254.     if (event.button != 0)
  1255.         return;
  1256.  
  1257.     var folderTree = GetFolderTree();
  1258.     var row = {};
  1259.     var col = {};
  1260.     var elt = {};
  1261.     folderTree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, elt);
  1262.     if (row.value == -1) {
  1263.       if (event.originalTarget.localName == "treecol")
  1264.         // clicking on the name column in the folder pane should not sort
  1265.         event.preventBubble();
  1266.       return;
  1267.     }
  1268.  
  1269.     if (elt.value == "twisty")
  1270.     {
  1271.         if (!(folderTree.treeBoxObject.view.isContainerOpen(row.value)))
  1272.         {
  1273.             var folderResource = GetFolderResource(folderTree, row.value);
  1274.  
  1275.             var isServer = GetFolderAttribute(folderTree, folderResource, "IsServer");
  1276.             if (isServer == "true")
  1277.             {
  1278.                 var msgFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
  1279.                 var server = msgFolder.server;
  1280.                 server.performExpand(msgWindow);
  1281.             }
  1282.             else
  1283.             {
  1284.                 var serverType = GetFolderAttribute(folderTree, folderResource, "ServerType");
  1285.                 if (serverType == "imap")
  1286.                 {
  1287.                     var imapFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgImapMailFolder);
  1288.                     imapFolder.performExpand(msgWindow);
  1289.                 }
  1290.             }
  1291.         }
  1292.     }
  1293.     else if ((event.originalTarget.localName == "slider") ||
  1294.              (event.originalTarget.localName == "scrollbarbutton")) {
  1295.       event.preventBubble();
  1296.     }
  1297.     else if (event.detail == 2) {
  1298.       FolderPaneDoubleClick(row.value, event);
  1299.     }
  1300.  
  1301. }
  1302.  
  1303. function FolderPaneDoubleClick(folderIndex, event)
  1304. {
  1305.     var folderTree = GetFolderTree();
  1306.     var folderResource = GetFolderResource(folderTree, folderIndex);
  1307.  
  1308.     var isServer = GetFolderAttribute(folderTree, folderResource, "IsServer");
  1309.     if (isServer == "true")
  1310.     {
  1311.       if (!(folderTree.treeBoxObject.view.isContainerOpen(folderIndex)))
  1312.       {
  1313.         var msgFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
  1314.         var server = msgFolder.server;
  1315.         server.performExpand(msgWindow);
  1316.       }
  1317.     }
  1318.     else 
  1319.     {
  1320.       // Open a new msg window only if we are double clicking on 
  1321.       // folders or newsgroups.
  1322.       MsgOpenNewWindowForFolder(folderResource.Value, -1 /* key */);
  1323.  
  1324.       // double clicking should not toggle the open / close state of the
  1325.       // folder.  this will happen if we don't prevent the event from
  1326.       // bubbling to the default handler in tree.xml
  1327.       event.preventBubble();
  1328.     }
  1329. }
  1330.  
  1331. function ChangeSelection(tree, newIndex)
  1332. {
  1333.     if(newIndex >= 0)
  1334.     {
  1335.         tree.view.selection.select(newIndex);
  1336.         tree.treeBoxObject.ensureRowIsVisible(newIndex);
  1337.     }
  1338. }
  1339.  
  1340. function GetSelectedFolders()
  1341. {
  1342.     var folderArray = [];
  1343.     var k = 0;
  1344.     var folderTree = GetFolderTree();
  1345.     var rangeCount = folderTree.view.selection.getRangeCount();
  1346.  
  1347.     for(var i = 0; i < rangeCount; i++)
  1348.     {
  1349.         var startIndex = {};
  1350.         var endIndex = {};
  1351.         folderTree.view.selection.getRangeAt(i, startIndex, endIndex);
  1352.         for (var j = startIndex.value; j <= endIndex.value; j++)
  1353.         {
  1354.             var folderResource = GetFolderResource(folderTree, j);
  1355.             folderArray[k++] = folderResource.Value;
  1356.         }
  1357.     }
  1358.  
  1359.     return folderArray;
  1360. }
  1361.  
  1362. function GetSelectedMsgFolders()
  1363. {
  1364.     var folderArray = [];
  1365.     var k = 0;
  1366.     var folderTree = GetFolderTree();
  1367.     var rangeCount = folderTree.view.selection.getRangeCount();
  1368.  
  1369.     for(var i = 0; i < rangeCount; i++)
  1370.     {
  1371.         var startIndex = {};
  1372.         var endIndex = {};
  1373.         folderTree.view.selection.getRangeAt(i, startIndex, endIndex);
  1374.         for (var j = startIndex.value; j <= endIndex.value; j++)
  1375.         {
  1376.           var folderResource = GetFolderResource(folderTree, j); 
  1377.           if (folderResource.Value != "http://home.netscape.com/NC-rdf#PageTitleFakeAccount") {
  1378.             var msgFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
  1379.             if(msgFolder)
  1380.               folderArray[k++] = msgFolder;
  1381.           }
  1382.         }
  1383.     }
  1384.  
  1385.     return folderArray;
  1386. }
  1387.  
  1388. function GetFirstSelectedMessage()
  1389. {
  1390.     try {
  1391.         return gDBView.URIForFirstSelectedMessage;
  1392.     }
  1393.     catch (ex) {
  1394.         return null;
  1395.     }
  1396. }
  1397.  
  1398. function GetSelectedIndices(dbView)
  1399. {
  1400.   try {
  1401.     var indicesArray = {}; 
  1402.     var length = {};
  1403.     dbView.getIndicesForSelection(indicesArray,length);
  1404.     return indicesArray.value;
  1405.   }
  1406.   catch (ex) {
  1407.     dump("ex = " + ex + "\n");
  1408.     return null;
  1409.   }
  1410. }
  1411.  
  1412. function GetSelectedMessages()
  1413. {
  1414.   try {
  1415.     var messageArray = {}; 
  1416.     var length = {};
  1417.     var view = GetDBView();
  1418.     view.getURIsForSelection(messageArray,length);
  1419.     if (length.value)
  1420.       return messageArray.value;
  1421.     else 
  1422.       return null;
  1423.   }
  1424.   catch (ex) {
  1425.     dump("ex = " + ex + "\n");
  1426.     return null;
  1427.   }
  1428. }
  1429.  
  1430. function GetLoadedMsgFolder()
  1431. {
  1432.     if (!gDBView) return null;
  1433.     return gDBView.msgFolder;
  1434. }
  1435.  
  1436. function GetLoadedMessage()
  1437. {
  1438.     try {
  1439.         return gDBView.URIForFirstSelectedMessage;
  1440.     }
  1441.     catch (ex) {
  1442.         return null;
  1443.     }
  1444. }
  1445.  
  1446. //Clear everything related to the current message. called after load start page.
  1447. function ClearMessageSelection()
  1448. {
  1449.   ClearThreadPaneSelection();
  1450. }
  1451.  
  1452. function GetCompositeDataSource(command)
  1453. {
  1454.     if (command == "GetNewMessages" || command == "NewFolder" || command == "MarkAllMessagesRead")
  1455.         return GetFolderDatasource();
  1456.  
  1457.     return null;
  1458. }
  1459.  
  1460. // Figures out how many messages are selected (hilighted - does not necessarily
  1461. // have the dotted outline) above a given index row value in the thread pane.
  1462. function NumberOfSelectedMessagesAboveCurrentIndex(index)
  1463. {
  1464.   var numberOfMessages = 0;
  1465.   var indicies = GetSelectedIndices(gDBView);
  1466.  
  1467.   if (indicies && indicies.length)
  1468.   {
  1469.     for (var i = 0; i < indicies.length; i++)
  1470.     {
  1471.       if (indicies[i] < index)
  1472.         ++numberOfMessages;
  1473.       else
  1474.         break;
  1475.     }
  1476.   }
  1477.   return numberOfMessages;
  1478. }
  1479.  
  1480. function SetNextMessageAfterDelete()
  1481. {
  1482.   var treeSelection = GetThreadTree().view.selection;
  1483.  
  1484.   if (treeSelection.isSelected(treeSelection.currentIndex))
  1485.     gNextMessageViewIndexAfterDelete = gDBView.msgToSelectAfterDelete;
  1486.   else if(gDBView.removeRowOnMoveOrDelete)
  1487.   {
  1488.     // Only set gThreadPaneDeleteOrMoveOccurred to true if the message was
  1489.     // truly moved to the trash or deleted, as opposed to an IMAP delete
  1490.     // (where it is only "marked as deleted".  This will prevent bug 142065.
  1491.     //
  1492.     // If it's an IMAP delete, then just set gNextMessageViewIndexAfterDelete
  1493.     // to treeSelection.currentIndex (where the outline is at) because nothing
  1494.     // was moved or deleted from the folder.
  1495.     gThreadPaneDeleteOrMoveOccurred = true;
  1496.     gNextMessageViewIndexAfterDelete = treeSelection.currentIndex - NumberOfSelectedMessagesAboveCurrentIndex(treeSelection.currentIndex);
  1497.   }
  1498.   else
  1499.     gNextMessageViewIndexAfterDelete = treeSelection.currentIndex;
  1500. }
  1501.  
  1502. function EnsureFolderIndex(builder, msgFolder)
  1503. {
  1504.   // try to get the index of the folder in the tree
  1505.   var index = builder.getIndexOfResource(msgFolder);
  1506.   if (index == -1) {
  1507.     // if we couldn't find the folder, open the parent
  1508.     builder.toggleOpenState(EnsureFolderIndex(builder, msgFolder.parent));
  1509.     index = builder.getIndexOfResource(msgFolder);
  1510.   }
  1511.   return index;
  1512. }
  1513.  
  1514. function SelectFolder(folderUri)
  1515. {
  1516.     var folderTree = GetFolderTree();
  1517.     var folderResource = RDF.GetResource(folderUri);
  1518.     var msgFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
  1519.  
  1520.     // before we can select a folder, we need to make sure it is "visible"
  1521.     // in the tree.  to do that, we need to ensure that all its
  1522.     // ancestors are expanded
  1523.     var folderIndex = EnsureFolderIndex(folderTree.builderView, msgFolder);
  1524.     ChangeSelection(folderTree, folderIndex);
  1525. }
  1526.  
  1527. function SelectMessage(messageUri)
  1528. {
  1529.   var msgHdr = messenger.msgHdrFromURI(messageUri);
  1530.   if (msgHdr)
  1531.     gDBView.selectMsgByKey(msgHdr.messageKey);
  1532. }
  1533.  
  1534. function ReloadWithAllParts()
  1535. {
  1536.   gDBView.reloadMessageWithAllParts();
  1537. }
  1538.  
  1539. function ReloadMessage()
  1540. {
  1541.   gDBView.reloadMessage();
  1542. }
  1543.  
  1544. function SetBusyCursor(window, enable)
  1545. {
  1546.     // setCursor() is only available for chrome windows.
  1547.     // However one of our frames is the start page which 
  1548.     // is a non-chrome window, so check if this window has a
  1549.     // setCursor method
  1550.     if ("setCursor" in window) {
  1551.         if (enable)
  1552.             window.setCursor("wait");
  1553.         else
  1554.             window.setCursor("auto");
  1555.     }
  1556.  
  1557.     var numFrames = window.frames.length;
  1558.     for(var i = 0; i < numFrames; i++)
  1559.         SetBusyCursor(window.frames[i], enable);
  1560. }
  1561.  
  1562. function GetDBView()
  1563. {
  1564.     return gDBView;
  1565. }
  1566.  
  1567. function GetFolderResource(tree, index)
  1568. {
  1569.     return tree.builderView.getResourceAtIndex(index);
  1570. }
  1571.  
  1572. function GetFolderIndex(tree, resource)
  1573. {
  1574.     return tree.builderView.getIndexOfResource(resource);
  1575. }
  1576.  
  1577. function GetFolderAttribute(tree, source, attribute)
  1578. {
  1579.     var property = RDF.GetResource("http://home.netscape.com/NC-rdf#" + attribute);
  1580.     var target = tree.database.GetTarget(source, property, true);
  1581.     if (target)
  1582.         target = target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1583.     return target;
  1584. }
  1585.  
  1586.